/*
 *  Copyright 2019-2020 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package me.zhengjie.modules.biz.service.impl;

import lombok.RequiredArgsConstructor;
import me.zhengjie.modules.biz.domain.*;
import me.zhengjie.modules.biz.repository.*;
import me.zhengjie.modules.biz.service.CabinetService;
import me.zhengjie.modules.biz.service.dto.CabinetDto;
import me.zhengjie.modules.biz.service.dto.CabinetQueryCriteria;
import me.zhengjie.modules.biz.service.mapstruct.CabinetMapper;
import me.zhengjie.modules.system.domain.User;
import me.zhengjie.modules.system.repository.UserRepository;
import me.zhengjie.modules.utils.LogUtils;
import me.zhengjie.utils.FileUtil;
import me.zhengjie.utils.PageUtil;
import me.zhengjie.utils.QueryHelp;
import me.zhengjie.utils.ValidationUtil;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.Predicate;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.System;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Admin
 * @website https://eladmin.vip
 * @description 服务实现
 * @date 2023-03-01
 **/
@Service
@RequiredArgsConstructor
public class CabinetServiceImpl implements CabinetService {

    private final CabinetRepository cabinetRepository;
    private final LabRepository labRepository;
    private final CabinetMapper cabinetMapper;
    private final UserRepository userRepository;
    private final HazardousChemicalsRepository hazardousChemicalsRepository;
    private final OpendoorrecordRepository opendoorrecordRepository;
    private final LockRepository lockRepository;

    @Override
    public Map<String, Object> queryAll(CabinetQueryCriteria criteria, Pageable pageable) {

        Specification<Cabinet> specification = (Specification<Cabinet>) (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<Predicate>();
            if (criteria.getDirectorId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("directorId"), criteria.getDirectorId()));
            }
            if (criteria.getStorehouseAdminId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("storehouseAdminId"), criteria.getStorehouseAdminId()));
            }
            if (criteria.getLabId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("labId"), criteria.getLabId()));
            }
            if (criteria.getName() != null) {
                predicates.add(criteriaBuilder.equal(root.get("name"), criteria.getName()));
            }
            predicates.add(criteriaBuilder.equal(root.get("type"), 1));
            return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        Page<Cabinet> page = cabinetRepository.findAll(specification, pageable);
        Page<CabinetDto> dto = page.map(cabinetMapper::toDto);
        List<CabinetDto> resultDto = dto.getContent();
        for (CabinetDto cabinetDto : resultDto) {
            if (cabinetDto.getLabId() != null) {
                Lab lab = labRepository.findById(cabinetDto.getLabId()).orElseGet(Lab::new);
                cabinetDto.setLabName(lab.getName());
            }
            if (cabinetDto.getStorehouseAdminId() != null) {
                User storehouseAdmin = userRepository.findById(Long.valueOf(cabinetDto.getStorehouseAdminId())).orElseGet(User::new);
                cabinetDto.setStoreHouseAdminName(storehouseAdmin.getUsername());
            }
            if (cabinetDto.getDirectorId() != null) {
                User director = userRepository.findById(Long.valueOf(cabinetDto.getDirectorId())).orElseGet(User::new);
                cabinetDto.setDirectorName(director.getUsername());
            }
        }
        return PageUtil.toPage(dto);
    }

    @Override
    public List<CabinetDto> queryAll(CabinetQueryCriteria criteria) {
        List<Cabinet> cabinetList = cabinetRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder));
        List<CabinetDto> cabinetDtoList = cabinetMapper.toDto(cabinetList);
        for (CabinetDto cabinetDto : cabinetDtoList) {
            if (cabinetDto.getLabId() != null) {
                Lab lab = labRepository.findById(cabinetDto.getLabId()).orElseGet(Lab::new);
                cabinetDto.setLabName(lab.getName());
            }
            if (cabinetDto.getStorehouseAdminId() != null) {
                User storehouseAdmin = userRepository.findById(Long.valueOf(cabinetDto.getStorehouseAdminId())).orElseGet(User::new);
                cabinetDto.setStoreHouseAdminName(storehouseAdmin.getUsername());
            }
            if (cabinetDto.getDirectorId() != null) {
                User director = userRepository.findById(Long.valueOf(cabinetDto.getDirectorId())).orElseGet(User::new);
                cabinetDto.setDirectorName(director.getUsername());
            }
        }
        return cabinetDtoList;
    }

    @Override
    @Transactional
    public CabinetDto findById(Integer id) {
        Cabinet cabinet = cabinetRepository.findById(id).orElseGet(Cabinet::new);
        ValidationUtil.isNull(cabinet.getId(), "Cabinet", "id", id);
        return cabinetMapper.toDto(cabinet);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CabinetDto create(Cabinet resources) {
        resources.setIsOpen(0);
        CabinetDto cabinetDto = cabinetMapper.toDto(cabinetRepository.save(resources));
        System.out.println(cabinetDto);
        System.out.println(resources);
        Lock lock = new Lock();
        lock.setIsOpen(0);
        lock.setCid(cabinetDto.getId());
        lock.setType(cabinetDto.getType());
        lock.setMac(cabinetDto.getMac());
        lockRepository.save(lock);

        return cabinetDto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(Cabinet resources) {
        Cabinet cabinet = cabinetRepository.findById(resources.getId()).orElseGet(Cabinet::new);
        ValidationUtil.isNull(cabinet.getId(), "Cabinet", "id", resources.getId());
        cabinet.copy(resources);
        cabinetRepository.save(resources);
        Lock lock = lockRepository.findByCidType(resources.getId(), resources.getType());
        if (lock != null) {
            lock.setMac(resources.getMac());
            lockRepository.save(lock);
        }
    }

    @Override
    public void deleteAll(Integer[] ids) {
        for (Integer id : ids) {
            Cabinet cabinet = cabinetRepository.findById(id).orElseGet(Cabinet::new);
            cabinetRepository.deleteById(id);
            Lock lock = lockRepository.findByCidType(cabinet.getId(), cabinet.getType());
            if (lock != null) lockRepository.deleteById(lock.getId());
        }
    }

    @Override
    public void download(List<CabinetDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (CabinetDto cabinet : all) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("名称", cabinet.getName());
            map.put("实验室", cabinet.getLabName());
            map.put("MAC地址", cabinet.getMac());
            map.put("仓库管理员", cabinet.getStoreHouseAdminName());
            map.put("主任", cabinet.getDirectorName());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    @Override
    public Map<String, Object> queryChemicalsList(CabinetQueryCriteria criteria, Pageable pageable) {
        Specification<Cabinet> specification = (Specification<Cabinet>) (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<Predicate>();
            if (criteria.getDirectorId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("directorId"), criteria.getDirectorId()));
            }
            if (criteria.getStorehouseAdminId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("storehouseAdminId"), criteria.getStorehouseAdminId()));
            }
            if (criteria.getLabId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("labId"), criteria.getLabId()));
            }
            if (criteria.getName() != null) {
                predicates.add(criteriaBuilder.equal(root.get("name"), criteria.getName()));
            }
//            predicates.add(criteriaBuilder.equal(root.get("type"), 1));
            return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        Page<Cabinet> page = cabinetRepository.findAll(specification, pageable);
        Page<CabinetDto> dto = page.map(cabinetMapper::toDto);
        List<CabinetDto> resultDto = dto.getContent();
        for (CabinetDto cabinetDto : resultDto) {
            if (cabinetDto.getLabId() != null) {
                Lab lab = labRepository.findById(cabinetDto.getLabId()).orElseGet(Lab::new);
                cabinetDto.setLabName(lab.getName());
            }
            if (cabinetDto.getStorehouseAdminId() != null) {
                User storehouseAdmin = userRepository.findById(Long.valueOf(cabinetDto.getStorehouseAdminId())).orElseGet(User::new);
                cabinetDto.setStoreHouseAdminName(storehouseAdmin.getUsername());
            }
            if (cabinetDto.getDirectorId() != null) {
                User director = userRepository.findById(Long.valueOf(cabinetDto.getDirectorId())).orElseGet(User::new);
                cabinetDto.setDirectorName(director.getUsername());
            }
        }
        return PageUtil.toPage(dto);
    }

    @Override
    public Map<String, Object> queryPoisonList(CabinetQueryCriteria criteria, Pageable pageable) {
        Specification<Cabinet> specification = (Specification<Cabinet>) (root, query, criteriaBuilder) -> {
            Predicate p1 = criteriaBuilder.equal(root.get("type").as(Integer.class), 2);
            Predicate p = criteriaBuilder.and(p1);
            return p;
        };
        Page<Cabinet> page = cabinetRepository.findAll(specification, pageable);
        Page<CabinetDto> dto = page.map(cabinetMapper::toDto);
        List<CabinetDto> resultDto = dto.getContent();
        for (CabinetDto cabinetDto : resultDto) {
            if (cabinetDto.getLabId() != null) {
                Lab lab = labRepository.findById(cabinetDto.getLabId()).orElseGet(Lab::new);
                cabinetDto.setLabName(lab.getName());
            }
            if (cabinetDto.getStorehouseAdminId() != null) {
                User storehouseAdmin = userRepository.findById(Long.valueOf(cabinetDto.getStorehouseAdminId())).orElseGet(User::new);
                cabinetDto.setStoreHouseAdminName(storehouseAdmin.getUsername());
            }
            if (cabinetDto.getDirectorId() != null) {
                User director = userRepository.findById(Long.valueOf(cabinetDto.getDirectorId())).orElseGet(User::new);
                cabinetDto.setDirectorName(director.getUsername());
            }
        }
        return PageUtil.toPage(dto);
    }

    @Override
    public void openDoor(Integer id) {
        if (id > 0) {
            Cabinet cabinet = cabinetRepository.findById(id).orElseGet(Cabinet::new);
            cabinet.setIsOpen(1);
            cabinetRepository.save(cabinet);

//            Opendoorrecord opendoorrecord = new Opendoorrecord();
//            opendoorrecord.setIsOpen(1);
//            opendoorrecord.setType(cabinet.getType());
//            opendoorrecord.setCId(id);
//            opendoorrecordRepository.save(opendoorrecord);

            new Thread(() -> {
                try {
                    Thread.sleep(20000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                //关门
                cabinet.setIsOpen(0);
                cabinetRepository.save(cabinet);
            }).start();
        }
    }


    @Override
    public void closeDoor(Integer id) {
        Cabinet cabinet = cabinetRepository.findById(id).orElseGet(Cabinet::new);
        cabinet.setIsOpen(0);
        cabinetRepository.save(cabinet);
//        Lock lock = lockRepository.findByCidType(cabinet.getId(), cabinet.getType());
//        lock.setIsOpen(0);
//        lockRepository.save(lock);
//        Opendoorrecord opendoorrecord = new Opendoorrecord();
//        opendoorrecord.setIsOpen(0);
//        opendoorrecord.setType(cabinet.getType());
//        opendoorrecord.setCId(id);
////        opendoorrecord.setUserId(Math.toIntExact(SecurityUtils.getCurrentUserId()));
//        opendoorrecordRepository.save(opendoorrecord);
    }

    @Override
    public List<CabinetDto> queryCabinetsByLabId(Integer labId) {
        List<Cabinet> cabinetList = cabinetRepository.findByLabId(labId);
        List<CabinetDto> cabinetDtoList = cabinetMapper.toDto(cabinetList);
        for (CabinetDto cabinetDto : cabinetDtoList) {
            if (cabinetDto.getLabId() != null) {
                Lab lab = labRepository.findById(cabinetDto.getLabId()).orElseGet(Lab::new);
                cabinetDto.setLabName(lab.getName());
            }
            if (cabinetDto.getStorehouseAdminId() != null) {
                User storehouseAdmin = userRepository.findById(Long.valueOf(cabinetDto.getStorehouseAdminId())).orElseGet(User::new);
                cabinetDto.setStoreHouseAdminName(storehouseAdmin.getUsername());
            }
            if (cabinetDto.getDirectorId() != null) {
                User director = userRepository.findById(Long.valueOf(cabinetDto.getDirectorId())).orElseGet(User::new);
                cabinetDto.setDirectorName(director.getUsername());
            }
        }
        return cabinetDtoList;
    }

    @Override
    public Map<String, Object> refrigeratorListPage(CabinetQueryCriteria criteria, Pageable pageable) {
        Specification<Cabinet> specification = (Specification<Cabinet>) (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<Predicate>();
            if (criteria.getDirectorId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("directorId"), criteria.getDirectorId()));
            }
            if (criteria.getStorehouseAdminId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("storehouseAdminId"), criteria.getStorehouseAdminId()));
            }
            if (criteria.getLabId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("labId"), criteria.getLabId()));
            }
            if (criteria.getName() != null) {
                predicates.add(criteriaBuilder.equal(root.get("name"), criteria.getName()));
            }
            predicates.add(criteriaBuilder.equal(root.get("type"), 2));
            return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
        };

        Page<Cabinet> page = cabinetRepository.findAll(specification, pageable);

        Page<CabinetDto> dto = page.map(cabinetMapper::toDto);
        List<CabinetDto> resultDto = dto.getContent();
        for (CabinetDto cabinetDto : resultDto) {
            if (cabinetDto.getLabId() != null) {
                Lab lab = labRepository.findById(cabinetDto.getLabId()).orElseGet(Lab::new);
                cabinetDto.setLabName(lab.getName());
            }
            if (cabinetDto.getStorehouseAdminId() != null) {
                User storehouseAdmin = userRepository.findById(Long.valueOf(cabinetDto.getStorehouseAdminId())).orElseGet(User::new);
                cabinetDto.setStoreHouseAdminName(storehouseAdmin.getUsername());
            }
            if (cabinetDto.getDirectorId() != null) {
                User director = userRepository.findById(Long.valueOf(cabinetDto.getDirectorId())).orElseGet(User::new);
                cabinetDto.setDirectorName(director.getUsername());
            }
        }
        return PageUtil.toPage(dto);
    }

    @Override
    public List<CabinetDto> refrigeratorList() {
        List<CabinetDto> cabinetDtoList = cabinetMapper.toDto(cabinetRepository.findByType(2));
        for (CabinetDto cabinetDto : cabinetDtoList) {
            if (cabinetDto.getLabId() != null) {
                Lab lab = labRepository.findById(cabinetDto.getLabId()).orElseGet(Lab::new);
                cabinetDto.setLabName(lab.getName());
            }
            if (cabinetDto.getStorehouseAdminId() != null) {
                User storehouseAdmin = userRepository.findById(Long.valueOf(cabinetDto.getStorehouseAdminId())).orElseGet(User::new);
                cabinetDto.setStoreHouseAdminName(storehouseAdmin.getUsername());
            }
            if (cabinetDto.getDirectorId() != null) {
                User director = userRepository.findById(Long.valueOf(cabinetDto.getDirectorId())).orElseGet(User::new);
                cabinetDto.setDirectorName(director.getUsername());
            }
        }
        return cabinetDtoList;
    }

    @Override
    public List<CabinetDto> cabinetAllList() {
        List<Cabinet> cabinetList = cabinetRepository.findAllList();
        List<CabinetDto> cabinetDtoList = cabinetMapper.toDto(cabinetList);
        for (CabinetDto cabinetDto : cabinetDtoList) {
            if (cabinetDto.getLabId() != null) {
                Lab lab = labRepository.findById(cabinetDto.getLabId()).orElseGet(Lab::new);
                cabinetDto.setLabName(lab.getName());
            }
            if (cabinetDto.getStorehouseAdminId() != null) {
                User storehouseAdmin = userRepository.findById(Long.valueOf(cabinetDto.getStorehouseAdminId())).orElseGet(User::new);
                cabinetDto.setStoreHouseAdminName(storehouseAdmin.getUsername());
            }
            if (cabinetDto.getDirectorId() != null) {
                User director = userRepository.findById(Long.valueOf(cabinetDto.getDirectorId())).orElseGet(User::new);
                cabinetDto.setDirectorName(director.getUsername());
            }
        }
        return cabinetDtoList;

    }

    @Override
    public List<Map<String, Object>> getCountByFloor() {
        return cabinetRepository.getCountByFloor();
    }

    @Override
    public List<Map<String, Object>> getRefrigeratorListByFloor(Integer floor) {
        return cabinetRepository.getRefrigeratorListByFloor(floor);
    }

    @Override
    public boolean setData(String mac, Float t, Float h) {
        Cabinet cabinet = cabinetRepository.findByMac(mac);
        if (cabinet != null) {
            cabinet.setTemperature(t);
            cabinet.setHumidity(h * 100);
            cabinetRepository.save(cabinet);
            return true;
        }

        return false;
    }

    @Override
    public Integer open(String mac) {
        Cabinet cabinet = cabinetRepository.findByMac(mac);

        if (cabinet == null) {
            LogUtils.writeToFile("cabinet mac:" + mac + " not found return 0");
            return 0;
        }

        LogUtils.writeToFile("cabinet " + mac + " founded and state is " + cabinet.getIsOpen());

        return cabinet.getIsOpen();
    }

    @Override
    public List<HazardousChemicals> hcList(Integer id) {
        return hazardousChemicalsRepository.findHcList(id);
    }


}
